import json

from kubernetes import client, config, utils
from os import path
from kubernetes.config.kube_config import _get_kube_config_loader
import yaml
from datetime import timezone,timedelta
import urllib3
from kubernetes.client.configuration import Configuration
from kubernetes.config.config_exception import ConfigException
#服务器获取api配置文件：cat ~/.kube/config
from kubernetes.client import V2MetricStatus

# urllib3.disable_warnings()

# def load_kube_config_from_dict(config_dict, context=None,
#                                client_configuration=None,
#                                persist_config=True):
#     """Loads authentication and cluster information from config_dict file
#     and stores them in kubernetes.client.configuration.
#
#     :param config_dict: Takes the config file as a dict.
#     :param context: set the active context. If is set to None, current_context
#         from config file will be used.
#     :param client_configuration: The kubernetes.client.Configuration to
#         set configs to.
#     :param persist_config: If True, config file will be updated when changed
#         (e.g GCP token refresh).
#     """
#
#     if config_dict is None:
#         raise ConfigException(
#             'Invalid kube-config dict. '
#             'No configuration found.')
#
#     loader = _get_kube_config_loader(
#         config_dict=config_dict, active_context=context,
#         persist_config=persist_config)
#
#     if client_configuration is None:
#         config = type.__call__(Configuration)
#         config.verify_ssl = False
#         loader.load_and_set(config)
#         Configuration.set_default(config)
#     else:
#         loader.load_and_set(client_configuration)

class K8sApi:
    def __init__(self, config_text):
        #config.kube_config.load_kube_config(config_file=config_file_path)

        configuration = client.Configuration()
        configuration.verify_ssl = False
        configuration.debug = False
        client.Configuration.set_default(configuration)

        config_text_dict = yaml.safe_load(config_text)
        config.kube_config.load_kube_config_from_dict(config_text_dict)
        # load_kube_config_from_dict(config_text_dict)

        self.client_api_clien = client.ApiClient()
        self.conn_appsv1api = client.AppsV1Api()
        self.conn_corev1api = client.CoreV1Api()


    def create_exec_ymal(self, yaml_info):
        """
        执行创建yaml内容
        @return:
        @param yaml_info: LIST[yaml_info]
        """
        return_data = utils.create_from_yaml(self.client_api_clien, yaml_objects=yaml_info)

        return True

    def get_nodes_status(self, name):
        """
        POD状态查询  kubectl describe node
        @param name:
        @return:
        """
        return_data = {}

        data = self.conn_corev1api.read_node_status(name, _request_timeout=1)
        # return_data['allocatable']  = data.status.allocatable
        return_data['ephemeral-storage'] = data.status.capacity['ephemeral-storage']
        return_data['pods'] = data.status.capacity['pods']

        # 获取
        return_data['node_taints_number'] = 0
        spec_taints_str = ""
        if data.spec.taints:
            for spec_taints_one in data.spec.taints:
                spec_taints_str += '%s:%s\n' %(spec_taints_one.key, spec_taints_one.value)
            return_data['node_taints_number'] = len(data.spec.taints)
        return_data['node_taints'] = spec_taints_str

        for conditions_one in data.status.conditions:
            if conditions_one.type == "Ready":
                return_data['kubelet_ready'] = conditions_one.status

        # return_data['allocatable'] = data['status']['allocatable']

        # return_data['allocatable']['ephemeral-storage'] = round(int(return_data['allocatable']['ephemeral-storage']) / 1024 /1024 / 1024)

        return return_data

    def get_top_nodes(self):
        return_data = {}
        ret = self.conn_corev1api.api_client.call_api(
            "/apis/metrics.k8s.io/v1beta1/nodes", 'GET', auth_settings=['BearerToken'],
            _preload_content=False
        )

        data = ret[0].data.decode('utf-8')
        data_ = json.loads(data)

        # return_data = {}
        # for items_one in  data_['items']:
        #     return_data[items_one['metadata']['name']] = items_one['usage']

        return data_['items']

    def dict_data_get_value(self, dict):
        return float(list(dict.values())[0].split()[0])

    def for_live_value_statistics(self, data_dict):
        return_data = 0
        for key, values in data_dict.items():
            if  key.find('pod=""') == -1 and key.find('container="POD"') == -1 and key.find('container=""') == -1:
                return_data += int(values.split()[0])
        return return_data

    async def get_node_cadvisor_info_monitoring(self, node_name):
        """
        返回节点监控信息
        @param node_name:
        @return:
        """
        return_data = {}
        self.get_node_cadvisor_info(node_name)

        return_data['use_memory'] = round(self.dict_data_get_value(
            self.node_cadvisor_target_dict['container_memory_working_set_bytes']) / 1024 / 1024 / 1024, 2)
        return_data['total_memory'] = round(
            self.dict_data_get_value(self.node_cadvisor_target_dict['machine_memory_bytes']) / 1024 / 1024 / 1024, 2)
        return_data['memory_use_text'] = '%s/%s' % (return_data['use_memory'], return_data['total_memory'])

        # CPU十秒内负载均值
        return_data['container_cpu_load_average_10s_'] = self.dict_data_get_value(
            self.node_cadvisor_target_dict['container_cpu_load_average_10s']) / 1000
        return_data['container_cpu_load_average_10s'] = self.for_live_value_statistics(
            self.node_cadvisor_target_dict['container_cpu_load_average_10s']) / 1000
        return_data['machine_cpu_cores'] = self.dict_data_get_value(self.node_cadvisor_target_dict['machine_cpu_cores'])
        return_data['cpu_use_text'] = '%s/%s' %(return_data['container_cpu_load_average_10s'], return_data['machine_cpu_cores'])

        # pod数量
        return_data['pod_number'] = 0
        for key in self.node_cadvisor_target_dict['container_threads']:
            if key.find('pod=""') == -1 and key.find('container="POD"') == -1 and key.find('container=""') == -1:
                return_data['pod_number'] += 1

        # 运行时IO统计
        return_data['container_fs_io_current'] = 0
        for key, values in self.node_cadvisor_target_dict['container_fs_io_current'].items():
            if key.find('pod=""') == -1 and key.find('container="POD"') == -1 and key.find('container=""') == -1:
                return_data['container_fs_io_current'] += int(values.split()[0])

        return return_data

    def get_node_cadvisor_info(self, node_name):
        """
        访问kubelet自带的cadvisor监控信息
        @param node_name:
        @return: dict{str：{}}
        """
        ret = self.conn_corev1api.api_client.call_api(
            "/api/v1/nodes/%s/proxy/metrics/cadvisor" %node_name, 'GET', auth_settings=['BearerToken'],
             _preload_content=False
        )

        dara = ret[0].data.decode('utf-8')

        self.node_cadvisor_target_dict = {}

        for dara_one in dara.split('\n'):
            if dara_one and dara_one[0] != '#':
                try:
                    key_haed_list = dara_one.split('{')
                    key_haed_body = key_haed_list[1].split('}')
                except IndexError:
                    key_list = dara_one.split()
                    self.node_cadvisor_target_dict[key_list[0]] = key_list[1]
                    continue
                try:
                    self.node_cadvisor_target_dict[key_haed_list[0]][key_haed_body[0]] = key_haed_body[1]
                except KeyError:
                    self.node_cadvisor_target_dict[key_haed_list[0]] = {key_haed_body[0]: key_haed_body[1]}

        return self.node_cadvisor_target_dict

    def list_node(self):
        """
        查询集群node信息
        @return:
        """
        ret = self.conn_corev1api.list_node(watch=False)
        return_data = []
        for i in ret.items:
            addresses = {}
            for addresses_ in i.status.addresses:
                addresses[addresses_.type] = addresses_.address

            addresses.update(
                {
                    "labels": i.metadata.labels,
                    "uid": i.metadata.uid,
                    "annotations": i.metadata.annotations,
                    "creation_timestamp": i.metadata.creation_timestamp
                }
            )

            return_data.append(addresses)

        return return_data


    def get_namespaces(self):
        ret = self.conn_corev1api.list_namespace(watch=False)
        return_data = []
        for i in ret.items:
            return_data.append({
                "name": i.metadata.name,
                "uid": i.metadata.uid,
                "annotations": i.metadata.annotations,
                "creation_timestamp": i.metadata.creation_timestamp
            })
        return return_data

    # def get_all_pod(self): ####################
    #     # 列出 namespaces
    #     ret = self.conn_corev1api.list_pod_for_all_namespaces(watch=False)
    #     return_data = []
    #     for i in ret.items:
    #         print("%s	%s	%s" % (i.status.pod_ip, i.metadata.namespace, i.metadata.name))
    #     return ""
    def read_namespaced_service(self,name, namespace):
        """
        查询到单个service信息
        @param name:
        @param namespace:
        @return:
        """
        ret = self.conn_corev1api.read_namespaced_service(name, namespace)
        return_data = {}
        # for i in ret.items:
        #     print(i)
        return return_data


    def get_service(self):
        # 列出所有的services
        ret = self.conn_corev1api.list_service_for_all_namespaces(watch=False)
        return_data = []
        for i in ret.items:
            ports_list = []
            ports = ""
            target_ports = ""
            node_ports = ""
            if i.spec.ports:
                for ports_obj in i.spec.ports:
                    ports_list.append({
                        "port": ports_obj.port,
                        "protocol": ports_obj.protocol,
                        "target_port": ports_obj.target_port,
                        "node_port": ports_obj.node_port,
                        "name": ports_obj.name
                    })
                    if ports_obj.port: ports += "%s " %ports_obj.port
                    if ports_obj.target_port: target_ports += "%s " %ports_obj.target_port
                    if ports_obj.node_port: node_ports += "%s " %ports_obj.node_port

            return_data.append({"kind": i.kind, "namespace": i.metadata.namespace, "name": i.metadata.name,
                                "selector": i.spec.selector, "type": i.spec.type,
                                "cluster_ip": i.spec.cluster_ip, "ports_info": ports_list, "target_ports": target_ports,
                                "node_ports": node_ports, "ports": ports,
                                "creation_timestamp": i.metadata.creation_timestamp})
        return return_data

    def get_service_yaml(self, name, namespace):
        """
        查询deployments yaml部署内容
        @param name: deployment_name
        @param namespace: deployment_namespace
        @return:
        """
        ret = self.conn_corev1api.api_client.call_api(
            "/api/v1/namespaces/%s/services/%s" %(namespace, name), 'GET', auth_settings=['BearerToken'],
            _preload_content=False
        )

        data = ret[0].data.decode('utf-8')
        data_ = json.loads(data)
        return data_

    # def create(self):        #       实验
    #     with open(path.join(path.dirname(__file__), "/root/deploy.yaml")) as f:
    #         dep = yaml.safe_load(f)
    #         resp = self.conn_appsv1api.create_namespaced_deployment(
    #             body=dep, namespace="default")
    #         print("Deployment created. status='%s'" % resp.metadata.name)
    #
    # def delete(self):            #       实验
    #     config.load_kube_config()
    #     resp = self.conn_corev1api.delete_namespaced_pod(namespace="default", name='nginx-pod')
    #     print("delete Pod ")

    def get_namespaced_pod(self, namespaced, label_selector=None):
        """
        查询空间下的pod
        @param namespaced:
        @return:
        """
        ret = self.conn_corev1api.list_namespaced_pod(namespaced, label_selector=label_selector)
        return_data = []
        for i in ret.items:
            return_data.append({
                "name": i.metadata.name,
                "uid": i.metadata.uid,
                "annotations": i.metadata.annotations,
                "creation_timestamp": i.metadata.creation_timestamp,
                "labels": i.metadata.labels,
                "node_name": i.spec.node_name,
                "pod_ip": i.status.pod_ip,
                "host_ip": i.status.host_ip,
                "phase": i.status.phase
            })
        return return_data

    def get_pods(self, label_selector=None):
        """
        查询所有pod
        @param label_selector: 标签过滤，格式：str: label1=value1,label2=value2
        @return:
        """
        # 列出所有的pod
        return_data = []
        ret = self.conn_corev1api.list_pod_for_all_namespaces(watch=False, label_selector=label_selector)
        for i in ret.items:
            if i.status.start_time:
                start_time = i.status.start_time.astimezone(timezone(timedelta(hours=8)))
            else:
                start_time = None
            return_data.append({"pod_ip": i.status.pod_ip,
                                "namespace": i.metadata.namespace,
                                "name": i.metadata.name,
                                "phase": i.status.phase,
                                "host_ip": i.status.host_ip,
                                "start_time": start_time,
                                "node_name": i.spec.node_name
                                })
        return return_data

    def get_pod_info(self, name, namespace):
        """
        查询pod信息
        @param name: pod_name
        @param namespace: namespace_name
        @return:
        """
        resp = self.conn_corev1api.read_namespaced_pod(name=name,
                                               namespace=namespace)
        return resp

    def get_pod_image(self, name, namespace):
        """
        查询pod镜像
        @param name: pod_name
        @param namespace: namespace_name
        @return:
        """
        obj = self.get_pod_info(name, namespace)
        return obj.spec.containers[0].image

    def get_all_deployment(self):
        """
        查询所有deployment
        @return:
        """
        data=[]
        # test = self.conn_appsv1api.list_deployment_for_all_namespaces()
        for dp in self.conn_appsv1api.list_deployment_for_all_namespaces().items:

            # 过滤获取端口
            ports_str = ""
            for containers_one in dp.spec.template.spec.containers:
                if containers_one.ports:
                    for ports_one in containers_one.ports:
                        ports_str += "%s/%s " %(ports_one.container_port, ports_one.protocol)
                else:
                    ports_str = ""

            # 过滤获取副本状态数
            state_replicas = [dp.status.replicas, dp.status.ready_replicas]
            for state_replicas_subscript in range(len(state_replicas)):
                if state_replicas[state_replicas_subscript] == None:
                    state_replicas[state_replicas_subscript] = 0
            dp.status.replicas, dp.status.ready_replicas = state_replicas

            if dp.spec.replicas == dp.status.ready_replicas == dp.status.replicas:
                status = "True"
            elif dp.spec.replicas == dp.status.ready_replicas:
                status = "Warning"
            else:
                status = "False"

            # 过滤获取更新时间
            conditions_time_ = {}
            for conditions_one in dp.status.conditions:
                conditions_time_[conditions_one.type] = conditions_one.last_update_time.strftime("%Y-%m-%d %H:%M:%S")

            # 其他指标获取
            conditions_time_.update({
                'name': dp.metadata.name,
                'namespace': dp.metadata.namespace,
                'creation_timestamp': dp.metadata.creation_timestamp,
                'status': status,
                'replicas_number': '%s/%s/%s' %(dp.status.ready_replicas, dp.status.replicas, dp.spec.replicas),
                'ports': ports_str
            })

            data.append(conditions_time_)
        return data

    def get_namespaced_deployment(self, namespaced, label_selector=None):
        """
        查询空间下的deployment
        @param namespaced:
        @return:
        """
        ret = self.conn_appsv1api.list_namespaced_deployment(namespaced, label_selector=label_selector)
        return_data = []
        for i in ret.items:
            containers = []
            containers_str = ""
            for i_one in i.spec.template.spec.containers:
                containers.append({"name": i_one.name, "image": i_one.image})
                containers_str += "%s " % i_one.image
            return_data.append({
                "available_replicas": i.status.available_replicas,
                "ready_replicas": i.status.ready_replicas,
                "name": i.metadata.name,
                "creation_timestamp": i.metadata.creation_timestamp,
                "labels": i.metadata.labels,
                "uid": i.metadata.uid,
                "containers": containers,
                "containers_str": containers_str
            })
        return return_data

    def replace_deployment_image(self, name, namespace, update_image):
        """
        镜像版本升级
        @param name: deployment_name
        @param namespace: deployment_namespace
        @param update_image:
        @return:
        """
        body = self.conn_appsv1api.read_namespaced_deployment(name, namespace, pretty="true")
        body.spec.template.spec.containers[0].image = update_image
        data = self.conn_appsv1api.replace_namespaced_deployment(name, namespace, body)
        return data

    def update_deployment_yaml(self, name, namespace, body):
        """
        更新deployments yaml
        @param name: deployment_name
        @param namespace: deployment_namespace
        @return:
        """
        data = self.conn_appsv1api.replace_namespaced_deployment(name, namespace, body)
        return data

    def get_deployment_yaml(self, name, namespace):
        """
        查询deployments yaml部署内容
        @param name: deployment_name
        @param namespace: deployment_namespace
        @return:
        """
        ret = self.conn_corev1api.api_client.call_api(
            "/apis/apps/v1/namespaces/%s/deployments/%s" %(namespace, name), 'GET', auth_settings=['BearerToken'],
            _preload_content=False
        )

        data = ret[0].data.decode('utf-8')
        data_ = json.loads(data)
        return data_

    def get_namespaced_stateful_set(self, namespaced, label_selector=None):
        """
        查询空间下的statefull_set（有状态副本）
        @param namespaced:
        @return:
        """
        data = []
        for dp in self.conn_appsv1api.list_namespaced_stateful_set(namespaced, label_selector=label_selector).items:

            containers = []
            containers_str = ""
            for i_one in dp.spec.template.spec.containers:
                containers.append({"name": i_one.name, "image": i_one.image})
                containers_str += "%s " % i_one.image

            data.append({
                "name": dp.metadata.name,
                "creation_timestamp": dp.metadata.creation_timestamp,
                "containers_str": containers_str,
                "containers": containers
            })
        return data

